/**
 * Title: CaptchaServiceImpl
 * Description: Copyright: Copyright (c) 2019 Company: Sharp
 *
 * @author Sharp
 * @date 2019-4-25 16:28
 * @description Project Name: Tanya
 * Package: com.srct.service.service.impl
 */
package com.sharp.authorization.service.captcha.impl;

import com.sharp.authorization.config.captcha.ICaptchaVerification;
import com.sharp.authorization.config.msg.IMsgVerification;
import com.sharp.authorization.constants.param.AccountParam;
import com.sharp.authorization.service.captcha.CaptchaService;
import com.sharp.authorization.service.token.TokenService;
import com.sharp.authorization.utils.captcha.CaptchaUtil;
import com.sharp.framework.service.cache.FrameCacheService;
import com.sharp.kernel.constant.err.ErrCodeSys;
import com.sharp.kernel.validate.Validator;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class CaptchaServiceImpl implements CaptchaService {

    private static final Map<String, ICaptchaVerification> CAPTCHA_CODE_TYPE_MAP = new ConcurrentHashMap<>(16);
    private static final String EXPIRED_TIMESTAMP = "_CAPTCHA_EXPIRED_TIMESTAMP";
    @Resource
    private TokenService tokenService;

    @Resource
    private FrameCacheService frameCacheService;

    @Override
    public BufferedImage generateCaptcha(String token, String type) {
        Validator.assertNotBlank(token, ErrCodeSys.PA_DATA_NOT_EXIST, "token");
        ICaptchaVerification captchaVerification = CAPTCHA_CODE_TYPE_MAP.get(type);
        Validator.assertNotNull(captchaVerification, ErrCodeSys.PA_DATA_NOT_EXIST, "图形验证码类型");
        StringBuffer code = new StringBuffer();
        BufferedImage image = CaptchaUtil.genRandomCodeImage(code);
        tokenService.updateTokenValue(token, type, code.toString().toLowerCase());
        Long timeout = System.currentTimeMillis();
        if (captchaVerification instanceof IMsgVerification) {
            timeout += (long) captchaVerification.getTimeout() * 10 * 1000;
        } else {
            timeout += captchaVerification.getTimeout() * 1000L;
        }
        tokenService.updateTokenValue(token, getExpiredTimestampTokenItem(type), timeout);
        return image;
    }

    private String getExpiredTimestampTokenItem(String type) {
        return type + EXPIRED_TIMESTAMP;
    }

    @Override
    public void validateCaptcha(String token, String type, String code) {
        Validator.assertNotBlank(token, ErrCodeSys.PA_DATA_NOT_EXIST, "token");
        Validator.assertNotBlank(code, ErrCodeSys.PA_DATA_NOT_EXIST, "图形验证码");
        String captcha;
        if (frameCacheService.getParamSwitch(AccountParam.TEST_MODE_VALIDATE_SWITCH)) {
            captcha = tokenService.getItem(token, type, String.class);
            Long timeStamp = tokenService.getItem(token, getExpiredTimestampTokenItem(type), Long.class);
            Validator.assertTrue(timeStamp > System.currentTimeMillis(), ErrCodeSys.SYS_ERR_MSG, "图形验证码已过期");
            Validator.assertMatch(captcha, code.toLowerCase(), ErrCodeSys.SYS_ERR_MSG, "验证码错误");
        } else {
            try {
                captcha = tokenService.getItem(token, type, String.class);
                Validator.assertMatch(captcha, code.toLowerCase(), ErrCodeSys.SYS_ERR_MSG, "验证码错误");
            } catch (Exception e) {
                captcha = frameCacheService.getParamValueAvail(AccountParam.TEST_MODE_VALIDATE_DEFAULT_CODE);
                Validator.assertMatch(captcha, code.toLowerCase(), ErrCodeSys.SYS_ERR_MSG, "验证码错误");
            }
        }
        cleanCaptchaToken(token, type);
    }

    private void cleanCaptchaToken(String token, String type) {
        tokenService.removeItem(token, type, getExpiredTimestampTokenItem(type));
    }

    @PostConstruct
    public void init() {
        Reflections reflections = new Reflections("com.sharp");
        Set<Class<? extends ICaptchaVerification>> msgVerificationClass = reflections.getSubTypesOf(ICaptchaVerification.class);
        for (Class<? extends ICaptchaVerification> clazz : msgVerificationClass) {
            if (Enum.class.isAssignableFrom(clazz)) {
                for (ICaptchaVerification enumItem : clazz.getEnumConstants()) {
                    if (StringUtils.isNotBlank(enumItem.name())) {
                        CAPTCHA_CODE_TYPE_MAP.put(enumItem.name(), enumItem);
                    }
                }
            }
        }
    }
}
